package com.yifeng.repo.controller.postman.work.task;

import com.fasterxml.jackson.databind.JsonNode;
import com.yifeng.repo.base.utils.common.FireHttpUtils;
import com.yifeng.repo.base.utils.common.FirePreconditions;
import com.yifeng.repo.controller.postman.work.biz.BizRequestAction;
import com.yifeng.repo.controller.postman.work.dto.HttpRequestParam;
import com.yifeng.repo.controller.postman.work.dto.PostTaskConf;
import com.yifeng.repo.controller.postman.work.dto.PostTaskDto;
import com.yifeng.repo.controller.postman.work.task.handler.PostInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by daibing on 2023/5/12.
 */
@Slf4j
public class HttpRequestTask<T> {
    /**
     * 构造函数传入
     */
    private final String requestId;
    private final PostTaskConf postTaskConf;
    private final PostInterceptor postInterceptor;
    private final HttpRequestParam httpRequestParam;
    private final Map<String, Object> bizSystemParams;
    private final BizRequestAction<T> bizRequestAction;

    public HttpRequestTask(String requestId, PostTaskConf postTaskConf, PostInterceptor postInterceptor,
                           HttpRequestParam httpRequestParam, Map<String, Object> bizSystemParams, BizRequestAction<T> bizRequestAction) {
        this.requestId = requestId;
        this.postTaskConf = postTaskConf;
        this.postInterceptor = postInterceptor;
        this.httpRequestParam = httpRequestParam;
        this.bizSystemParams = bizSystemParams;
        this.bizRequestAction = bizRequestAction;
    }

    public T doRun() {
        // 1. 初始化投递任务对象：请求参数
        PostTaskDto postTaskDto = new PostTaskDto();
        postTaskDto.setRequestId(requestId);
        postTaskDto.setHttpRequestParam(httpRequestParam);
        postTaskDto.setBizSystemParams(postInterceptor.modify(bizSystemParams));

        // 2. 投递拦截器应用处理：处理占位符
        postInterceptor.apply(postTaskDto);

        // 3. 发起请求
        Map<String, String> result = doRunByRetry(
                postTaskDto.getMethod(),
                postTaskDto.getUrl(),
                postTaskDto.getHeader(),
                postTaskDto.getBody(),
                postTaskConf.getTryTimes(),
                postTaskConf.getTimeoutMillis()
        );
        String responseBody = result.remove(FireHttpUtils.RESPONSE_BODY);
        JsonNode output = bizRequestAction.parseResponseResult(result, responseBody);

        // 4. 响应处理
        FirePreconditions.checkState(
                bizRequestAction.checkResponseBody(output),
                String.format("Post response data is not ok: requestId=%s, uri=%s, result=%s", requestId, httpRequestParam.getUri(), result));
        FirePreconditions.checkState(
                bizRequestAction.checkResponseSign(output),
                String.format("Post response data is not safe: requestId=%s, uri=%s, result=%s", requestId, httpRequestParam.getUri(), result));
        return bizRequestAction.extractResponseData(output);
    }

    private Map<String, String> doRunByRetry(HttpMethod method, String url, Map<String, String> header, byte[] body,
                                             int tryTimes, int timeoutMillis) {
        for (int i = 0; i < tryTimes; i++) {
            try {
                return this.doRun(method, url, header, body, timeoutMillis);
            } catch (Throwable e) {
                if (tryTimes - 1 == i) {
                    throw e;
                }
            }
        }
        return new HashMap<>();
    }

    private Map<String, String> doRun(HttpMethod method, String url, Map<String, String> header, byte[] body,
                                      int timeoutMillis) {
        Map<String, String> result = null;
        try {
            if (HttpMethod.GET == method && body.length > 0) {
                url += "?" + new String(body);
            }
            FireHttpUtils httpClient = new FireHttpUtils(url, timeoutMillis, true);
            switch (method) {
                case GET:
                    result = httpClient.get(header);
                    break;
                case PUT:
                    result = httpClient.put(header, body);
                    break;
                case POST:
                    result = httpClient.post(header, body);
                    break;
                case DELETE:
                    result = httpClient.delete(header, body);
                    break;
                default:
            }
        } catch (IOException e) {
            throw new RuntimeException(String.format("Postman client %s http %s failed: url：%s, exception: %s", requestId, method, url, e));
        }
        return result;
    }

}
